// Copyright 2010, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_LAST_ERROR_MSG_H_
#define LIBV_LAST_ERROR_MSG_H_

#include <errno.h>
#include <string.h>
#include <string>



namespace libv
{

/** Переносимая версия функции strerror, возвращающая текстовое описание
  * ошибки, соответствующей коду errno == errcode.
  * @warning Не предназначена для использования с кодами от GetLastError().
  * @sa OsErrorCode2String.
  *
  * Метод потокобезопасен и не генерирует исключений. */
inline ::std::string StrError(const int errcode = errno)
{
    char msg_buf[1024 + 1];

#if defined(_WIN32)
    if ( strerror_s(msg_buf, sizeof(msg_buf), errcode) != 0 )
        return ::std::string("Unknown error");

#elif ( _XOPEN_SOURCE >= 600 && !_GNU_SOURCE )
    if ( strerror_r(errcode, msg_buf, sizeof(msg_buf)) == -1 )
        return ::std::string("Unknown error");

#else  // GNU-extension
    const char* const msg = strerror_r(errcode, msg_buf, sizeof(msg_buf));
    if ( msg_buf != msg )
        strncpy(msg_buf, msg, sizeof(msg_buf));

#endif

    msg_buf[sizeof(msg_buf) - 1] = '\0';
    return ::std::string(msg_buf);
}


/** Кроссплатформенный перевод кода ошибки в строку с описанием ошибки.
  * Для Windows на вход нужно подавать результат функции GetLastError().
  * В линукс — значение errno.
  *
  * Метод потокобезопасен и не генерирует исключений. */
inline ::std::string OsErrorCode2String(const int err)
{
#ifdef _WIN32
    const DWORD max_msg_len = 1024 + 1;
    char buf[max_msg_len];
    const DWORD msg_len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
                                         FORMAT_MESSAGE_IGNORE_INSERTS |
                                         FORMAT_MESSAGE_MAX_WIDTH_MASK,
                                         NULL,
                                         err,
                                         MAKELANGID(LANG_NEUTRAL,
                                                    SUBLANG_NEUTRAL),
                                         (LPSTR)buf,
                                         max_msg_len,
                                         NULL);

    return ::std::string(buf, msg_len);
#else
    return StrError(err);
#endif
}

}  // libv

#endif  // LIBV_LAST_ERROR_MSG_H_
